from django.shortcuts import render

# Create your views here.
import os
import time
import hashlib
import datetime
from rest_framework.viewsets import GenericViewSet
from utils.Ops_mixins import OpsListModelMixin, OpsUpdateModelMixin, OpsCreateModelMixin, OpsDestroyModelMixin
from utils.Ops_responses import OpsResponse
from utils.Ops_page import HostPage
from charts.models import Host
from dev.models import Monitorpy, Fork_version, Log_audit
from dev.serializer import HostModelSerializer, MonitorpyModelSerializer, FileForkViewModelSerializer, \
    LogsViewModelSerializer
from rest_framework.decorators import action
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter
from utils.files_handle import upload_handle, delete_handle, for_push_file, write_file, get_linux_result
from APS.views import scheduler, cron_job
from libs.connect_ssh.devops_ssh import do_ssh
from libs.alarmspy.dl_mysql import get_dl_mysql
from utils.log_db import handle_log
from celery_task.ops_task import fork_files
from celery.result import AsyncResult
from celery_task.celery import app


conn, cursor = get_dl_mysql()


class HostView(OpsListModelMixin, GenericViewSet):
    '''
    获取所有正常主机
    '''
    queryset = Host.objects.filter(host_status=0, is_manage=0)
    serializer_class = HostModelSerializer


class MonitorpyView(OpsListModelMixin, OpsUpdateModelMixin, OpsDestroyModelMixin, GenericViewSet):
    '''
    监控脚本视图类
    '''
    queryset = Monitorpy.objects.filter()
    serializer_class = MonitorpyModelSerializer

    pagination_class = HostPage
    filter_backends = [DjangoFilterBackend, SearchFilter]
    filterset_fields = ['host_ip']
    search_fields = ['py_title', 'file_name', 'py_describe']

    def update(self, request, *args, **kwargs):
        '''
        修改定时任务
        :param request:
        :param args:
        :param kwargs:
        :return:
        '''
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        if not serializer.is_valid():
            return OpsResponse()
        res = self.perform_update(serializer)
        # 日志审计
        handle_log(str(request.path), f"修改 {instance if instance else '00000'}")
        # 修改定时任务：删除 重新创建
        host_ip, file_name = str(instance).split('|')
        file_name_id = file_name + '_' + host_ip
        # # 删除
        scheduler.remove_job(file_name_id, jobstore=None)
        # # 新建
        scheduler.add_job(cron_job, trigger='interval', args=[file_name, request.data, ], seconds=int(request.data.get('py_timer')),
                          id=file_name_id, replace_existing=True, max_instances=100, misfire_grace_time=5)
        scheduler.resume_job(file_name_id, jobstore=None)
        return res or OpsResponse(data=serializer.data)

    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        # 删除服务器中的文件数据
        host_ip, file_name = str(instance).split('|')
        delete_handle(host_ip, file_name)
        instance.delete()
        handle_log(str(request.path), f'删除 {instance}')
        # 删除定时任务
        file_name_id = file_name + '_' + host_ip
        scheduler.remove_job(file_name_id, jobstore=None)
        return OpsResponse()

    @action(methods=['POST'], detail=False)
    def upload_file(self, request):
        file_name = request.FILES.get('myfile')  # 文件名
        req = request.data
        host_ip = req['host_ip']
        Monitorpy.objects.create(py_title=req['py_title'], file_name=file_name, py_describe=req['py_describe'],
                                 py_timer=req['py_timer'], host_ip=req['host_ip'])
        # 远程操作文件上传
        handle_log(str(request.path), f'上传 {file_name} 到 {host_ip}')
        upload_handle(file_name, str(file_name), host_ip, req)
        return OpsResponse()


class ConfigsView(GenericViewSet):
    '''
    修改文件、及批量执行 linux 命令
    '''
    @action(methods=['POST'], detail=False)
    def pull_info(self, request):
        info = request.POST
        ssh, sftp = do_ssh(info['hostlist'])
        with sftp.open(info['path'], "rb") as fl:
            # fl.prefetch()
            file_info = fl.read().replace(b'\r\r\n', b'\r\n')
        return OpsResponse(file_info=file_info)

    @action(methods=['POST'], detail=False)
    def push_info(self, request):
        info = request.POST
        file_ip = info['hostlist'].split(',')
        file_path = info['path']
        file_textarea = info['textarea']

        # 将要上传的文件写入本地
        localpath = r'H:\py\DevOps\DevOps_api\DevOps_api\media\filename.txt'
        write_file(localpath, 'w', file_textarea)
        # 循环上传文件
        handle_log(str(request.path), f'上传到 {file_path}  主机：{file_ip}')
        textarea = for_push_file(file_ip, file_path, localpath)
        return OpsResponse(file_info=textarea)

    @action(methods=['POST'], detail=False)
    def get_linux_commands(self, request):
        info = request.POST
        file_ip = info['hostlist'].split(',')
        commands = info['path']
        handle_log(str(request.path), f'执行命令 {commands}  主机：{file_ip}')
        # 循环获取指令结果
        textarea = """"""
        for ip in file_ip:
            ssh, sftp = do_ssh(ip)
            result = get_linux_result(ssh, commands)
            textarea += ip + '\n' + result + '\n' + '\n'

        return OpsResponse(file_info=textarea)


class FileForkView(OpsListModelMixin, GenericViewSet):
    '''
    分发文件界面接口
    '''
    queryset = Fork_version.objects.filter()
    serializer_class = FileForkViewModelSerializer

    @action(methods=['POST'], detail=False)
    def send_file(self, request):
        # 获取前端数据
        file_name = request.FILES.get('myfile')  # 文件名
        req = request.data
        hostlist = req['hostlist'].split(',')
        user_id = req['user_id']
        # 目标路径
        filepath = req['path']
        filepath = filepath if filepath[-1] == '/' else filepath + '/'
        # filepath = filepath + f'{str(file_name)}'
        # print('token', req['token'])
        localpath = r'H:\py\DevOps\DevOps_api\DevOps_api\media\filename.txt'
        # 写入本地
        write_file(localpath, 'wb', file_name)
        handle_log(str(request.path), f'上传目录 {filepath}  主机：{hostlist}')
        # 分发压缩包 || 分发单个文件
        if str(file_name).split('.')[-1].lower() == 'zip':
            textarea = self.frok_zip(hostlist, localpath, filepath, str(file_name), user_id)
        else:
            textarea = for_push_file(hostlist, filepath, localpath)
        return OpsResponse(file_info=textarea)

    @action(methods=['POST'], detail=False)
    def back_file(self, request):
        # 获取前端数据
        req = request.data
        version_list = req['versionlist'].split(',')
        user_id = req['user_id']
        hostlist = req['hostlist'].split(',')
        # 排序
        version_dit = {int(i.split('_')[0]): i for i in version_list}
        sort = sorted(version_dit.keys(), reverse=True)
        version_list = [version_dit[st] for st in sort]
        # 执行回退命令
        handle_log(str(request.path), f'回退版本 {version_list}  主机：{hostlist}')
        textarea = self.back_fork(hostlist, version_list)
        return OpsResponse(file_info=textarea)

    def frok_zip(self, hostlist, localpath, filepath, file_name, user_id):
        '''
        分发 zip 文件的逻辑
        指定上传文件：/ysg/back-up/back.zip
        :param hostlist:
        :param filepath:
        :return:
        '''
        # 生成版本号、入库
        ver_name = str(int(time.time())) + '_' + file_name
        new_time = datetime.datetime.now()
        textarea = """"""
        for ip in hostlist:
            delay_id = fork_files.delay(ip, localpath, textarea, filepath, ver_name)
            cursor.execute(f'insert into ops_delay_task(delay_id) values("{delay_id}")')
        cursor.execute(
            f'insert into ops_fork_version(ver_name, user_id, ver_time) values("{ver_name}", "{user_id}", "{new_time}")')
        conn.commit()
        return textarea

    def back_fork(self, hostlist, version_list):
        textarea = """"""
        for ip in hostlist:
            for version_name in version_list:
                ssh, sftp = do_ssh(ip)
                command = f'python3 /ysg/monitorpy/systempy/fallback.py {version_name}'
                textarea += ip + '\n' + command + '\n'
                result = get_linux_result(ssh, command)
                textarea += ip + '\n' + result + '\n' + '\n'

        # 删除回退的版本号
        # for version_name in version_list:
        #     cursor.execute(f'delete from ops_fork_version where ver_name="{version_name}"')
        # conn.commit()
        return textarea

    @action(methods=['GET'], detail=False)
    def delay_textarea(self, request):
        cursor.execute(f'select delay_id from ops_delay_task')
        delay_id_list = cursor.fetchall()
        if not delay_id_list: return OpsResponse(code=9999)
        result = """"""
        for id in delay_id_list:
            res = AsyncResult(id=id[0], app=app)
            if res.successful():
                result += res.get()
                cursor.execute(f'delete from ops_delay_task where delay_id="{id[0]}"')
            # elif res.failed():
            #     print('任务失败')
            # elif res.status == 'PENDING':
            #     print('任务等待被执行')
            # elif res.status == 'RETRY':
            #     print('任务异常后正在重试')
            # elif res.status == 'STARTED':
            #     print('任务已经开始被执行')
        conn.commit()
        return OpsResponse(file_info=result)


class LogsView(OpsListModelMixin, GenericViewSet):
    queryset = Log_audit.objects.all().order_by('-id')
    serializer_class = LogsViewModelSerializer

    pagination_class = HostPage
    filter_backends = [SearchFilter]
    search_fields = ['tag_name', 'log_info', 'user_name', 'handle_time']
